# 随机梯度下降法实现线性回归
import numpy as np

# ==================== 数据准备阶段 ====================
# 设置随机种子保证每次运行结果一致
np.random.seed(42)

# 生成特征数据X：100个样本，每个样本1个特征，取值范围[0,2)
X = 2 * np.random.rand(100, 1)

# 生成标签数据y：真实模型为y=4+3X+高斯噪声
y = 4 + 3 * X + np.random.randn(100, 1)

# 为X添加一列全1的偏置项
X_b = np.c_[np.ones((100, 1)), X]  # shape: (100, 2)

# ==================== 超参数设置 ====================
n_epochs = 50           # 遍历整个数据集的轮次
m = len(X_b)            # 样本数量
initial_learning_rate = 0.1  # 初始学习率
t0, t1 = 5, 50          # 学习率衰减参数

# ==================== 模型参数初始化 ====================
theta = np.random.randn(2, 1)  # 随机初始化参数

# ==================== 随机梯度下降迭代 ====================
for epoch in range(n_epochs):
    # 计算当前epoch的学习率(随时间衰减)
    learning_rate = initial_learning_rate / (t0 + epoch)

    # 在每个epoch中，随机遍历所有样本
    for i in range(m):
        # 随机选择一个样本
        random_index = np.random.randint(m)
        xi = X_b[random_index:random_index + 1]
        yi = y[random_index:random_index + 1]

        # 计算单个样本的梯度
        gradients = xi.T.dot(xi.dot(theta) - yi)

        # 更新参数
        theta = theta - learning_rate * gradients

    # 每10次迭代打印当前参数和损失
    if epoch % 10 == 0:
        loss = np.mean((X_b.dot(theta) - y) ** 2)
        print(f"Epoch {epoch}: theta={theta.T}, loss={loss:.4f}")

# ==================== 输出最终结果 ====================
# 打印训练后得到的参数，应该接近真实值[4,3]
print("训练得到的参数:")
print(theta.T)
